﻿
Imports System.IO
Imports System.Net
Imports osi.root.delegates
Imports osi.root.connector
Imports osi.root.formation
Imports osi.root.lock
Imports osi.root.procedure
Imports osi.root.utils
Imports osi.service.commander.constants

Public Class questioner
    Public Shared ReadOnly [default] As questioner
    Private ReadOnly bytes As binder(Of Func(Of Byte(), 
                                                pointer(Of Byte()), 
                                                event_comb), 
                                        questioner)
    Private ReadOnly http_get As binder(Of Func(Of String, 
                                                   pointer(Of HttpStatusCode), 
                                                   pointer(Of WebHeaderCollection), 
                                                   pointer(Of String), 
                                                   event_comb), 
                                           questioner)
    Private ReadOnly http_post As binder(Of _func(Of Stream, 
                                                     Int64, 
                                                     pointer(Of HttpStatusCode), 
                                                     pointer(Of WebHeaderCollection), 
                                                     pointer(Of String), 
                                                     event_comb), 
                                            questioner)
    Private ReadOnly last_error As atomic_int
    Private ReadOnly expired As wrapper(Of singleentry)

    Shared Sub New()
        [default] = New questioner()
    End Sub

    Public Sub New()
        bytes = New binder(Of Func(Of Byte(), 
                                      pointer(Of Byte()), 
                                      event_comb), 
                              questioner)()
        http_get = New binder(Of Func(Of String, 
                                         pointer(Of HttpStatusCode), 
                                         pointer(Of WebHeaderCollection), 
                                         pointer(Of String), 
                                         event_comb), 
                                 questioner)()
        http_post = New binder(Of _func(Of Stream, 
                                           Int64, 
                                           pointer(Of HttpStatusCode), 
                                           pointer(Of WebHeaderCollection), 
                                           pointer(Of String), 
                                           event_comb), 
                                  questioner)()
        last_error = New atomic_int()
        expired = New wrapper(Of singleentry)()
    End Sub

    Public Sub New(ByVal q As Func(Of Byte(), pointer(Of Byte()), event_comb))
        Me.New()
        bytes.set_local(q)
    End Sub

    Public Sub New(ByVal q As Func(Of String, 
                                      pointer(Of HttpStatusCode), 
                                      pointer(Of WebHeaderCollection), 
                                      pointer(Of String), 
                                      event_comb))
        Me.New()
        http_get.set_local(q)
    End Sub

    Public Sub New(ByVal q As _func(Of Stream, 
                                       Int64, 
                                       pointer(Of HttpStatusCode), 
                                       pointer(Of WebHeaderCollection), 
                                       pointer(Of String), 
                                       event_comb))
        Me.New()
        http_post.set_local(q)
    End Sub

    Private Function q(ByVal request As command,
                       ByVal response As command) As event_comb
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If bytes.has_local_value() Then
                                      ec = _question.question(request, bytes.local(), response)
                                  ElseIf http_get.has_local_value() Then
                                      ec = _question.question(request, http_get.local(), response)
                                  ElseIf http_post.has_local_value() Then
                                      ec = _question.question(request, http_post.local(), response)
                                  ElseIf bytes.has_value() Then
                                      ec = _question.question(request, bytes.get(), response)
                                  ElseIf http_get.has_value() Then
                                      ec = _question.question(request, http_get.get(), response)
                                  ElseIf http_post.has_value() Then
                                      ec = _question.question(request, http_post.get(), response)
                                  Else
                                      Return assert(False)
                                  End If
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As command) As event_comb
        Get
            Dim ec As event_comb = Nothing
            Return New event_comb(Function() As Boolean
                                      If (+last_error) >= last_error_count Then
                                          Return False
                                      Else
                                          ec = q(request, response)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      End If
                                  End Function,
                                  Function() As Boolean
                                      If ec.end_result() Then
                                          Return goto_end()
                                      Else
                                          If last_error.increment() = last_error_count Then
                                              start_ping()
                                          End If
                                          Return False
                                      End If
                                  End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As pointer(Of command)) As event_comb
        Get
            Dim r As command = Nothing
            Dim ec As event_comb = Nothing
            Return New event_comb(Function() As Boolean
                                      r = New command()
                                      ec = question(request, r)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End Function,
                                  Function() As Boolean
                                      Return ec.end_result() AndAlso
                                             eva(response, r) AndAlso
                                             goto_end()
                                  End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As Func(Of command, Boolean)) As event_comb
        Get
            Dim r As pointer(Of command) = Nothing
            Dim ec As event_comb = Nothing
            Return New event_comb(Function() As Boolean
                                      r = New pointer(Of command)()
                                      ec = question(request, r)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End Function,
                                  Function() As Boolean
                                      Return ec.end_result() AndAlso
                                             Not (+r) Is Nothing AndAlso
                                             (+r).action_is(constants.response.success) AndAlso
                                             (response Is Nothing OrElse response(+r)) AndAlso
                                             goto_end()
                                  End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command,
                                              ByVal response As Func(Of Boolean)) As event_comb
        Get
            Return question(request,
                            Function(c As command) As Boolean
                                Return response Is Nothing OrElse response()
                            End Function)
        End Get
    End Property

    Default Public ReadOnly Property question(ByVal request As command) As event_comb
        Get
            Return question(request, Function() True)
        End Get
    End Property

    Public Sub [stop]()
        expired.mark_in_use()
    End Sub

    Private Function ping_once() As event_comb
        Dim ec As event_comb = Nothing
        Dim res As command = Nothing
        Return New event_comb(Function() As Boolean
                                  Dim req As command = Nothing
                                  req = New command()
                                  req.attach(action.ping)
                                  res = New command()
                                  ec = q(req, res)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         res.action_is(response.success) AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Sub start_ping()
        Dim ec As event_comb = Nothing
        begin_lifetime_event_comb(expired,
                                  Function() As Boolean
                                      ec = ping_once()
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End Function,
                                  Function() As Boolean
                                      If ec.end_result() Then
                                          last_error.set(0)
                                          Return goto_end()
                                      Else
                                          Return waitfor(ping_interval_ms) AndAlso
                                                 goto_begin()
                                      End If
                                  End Function)
    End Sub
End Class
